package com.geline.mybatisplus.mapper;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.geline.mybatisplus.handler.MetaEntityHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

@Deprecated
@Transactional
@Slf4j
public class JdbcTemplateCurdMapper implements CurdMapper{

    private JdbcTemplate jdbcTemplate;
    private MetaEntityHandler metaEntityHandler;

    public JdbcTemplateCurdMapper(JdbcTemplate jdbcTemplate, MetaEntityHandler metaEntityHandler) {
        this.jdbcTemplate = jdbcTemplate;
        this.metaEntityHandler = metaEntityHandler;
    }

    @Override
    public <T> boolean save(T entity) {
        TableName annotation = entity.getClass().getAnnotation(TableName.class);
        Objects.requireNonNull(annotation, "no @TableName : " + entity.getClass().getSimpleName());
        Field[] declaredFields = entity.getClass().getDeclaredFields();
        IdType idType = IdType.AUTO;
        for(Field field : declaredFields){
            TableId tableId = field.getAnnotation(TableId.class);
            if(tableId!= null){
                idType = tableId.type();
            }
        }
        metaEntityHandler.insertFill(entity);
        insert(annotation.value(), BeanMap.create(entity), idType);
        return true;
    }

    @Override
    public <T> boolean updateById(T entity) {
        TableName annotation = entity.getClass().getAnnotation(TableName.class);
        Objects.requireNonNull(annotation, "no @TableName : " + entity.getClass().getSimpleName());
        updateById(annotation.value(), BeanMap.create(entity));
        return true;
    }

    @Override
    public <T> boolean saveBatch(Collection<T> entityList) {
        for (T entity : entityList) {
            save(entity);
        }
        return true;
    }

    @Override
    public <T> T getById(Serializable id, Class<T> entityClass) {
        TableName annotation = entityClass.getAnnotation(TableName.class);
        Objects.requireNonNull(annotation, "no @TableName : " + entityClass.getSimpleName());
        String idValue = id instanceof CharSequence ? StrUtil.wrap(id.toString(), "'") : id.toString();
        String sql = String.format("select * from %s where %s=%s", annotation.value(), "id", idValue);
        T bean = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(entityClass));
        return bean;
    }

    @Override
    public <T> boolean removeById(Object id, Class<T> entityClass) {
        TableName annotation = entityClass.getAnnotation(TableName.class);
        Objects.requireNonNull(annotation, "no @TableName : " + entityClass.getSimpleName());
        String idValue = id instanceof CharSequence ? StrUtil.wrap(id.toString(), "'") : id.toString();
        String sql = String.format("delete from %s where %s=%s", annotation.value(), "id", idValue);
        return jdbcTemplate.update(sql)>0;
    }

    @Override
    public <T> boolean removeByIds(Collection<?> ids, Class<T> entityClass) {
        for (Object id : ids) {
            removeById(id, entityClass);
        }
        return true;
    }

    private boolean insert(String tableName, Map<String, Object> data, IdType idType) {
        List<Object> values = new ArrayList<>();
        String sql = makeInsertSql(tableName, data, idType, values);
        return jdbcTemplate.update(sql, values.toArray())>0;
    }

    private boolean updateById(String tableName, Map<String, Object> data) {
        List<Object> values = new ArrayList<>();
        String sql = makeUpdateSql(tableName, data, values);
        return jdbcTemplate.update(sql, values.toArray())>0;
    }

    private String makeInsertSql(String tableName, Map<String, Object> data, IdType idType, List<Object> values) {
        StringBuilder sql = (new StringBuilder()).append("insert into ").append(tableName).append("(");
        int nameCount = 0;

        for(String name : data.keySet()) {
            if(name.equals("id")){
                if(idType== IdType.AUTO){
                    continue;
                }
            }
            if (nameCount > 0) {
                sql.append(",");
            }
            ++nameCount;
            sql.append(StrUtil.toUnderlineCase(name));
            Object value = data.get(name);
            if(name.equals("id")){
                if(idType==IdType.AUTO){
                    continue;
                }else if(idType==IdType.ASSIGN_ID){
                    values.add(System.currentTimeMillis());
                }else if(idType==IdType.ASSIGN_UUID){
                    values.add(IdUtil.fastSimpleUUID());
                }else {
                    throw new RuntimeException("can not handle IdType : "+idType);
                }
            }else {
                values.add(value);
            }
        }
        sql.append(") values (");
        for(int i = 0; i < nameCount; ++i) {
            if (i != 0) {
                sql.append(",");
            }
            sql.append("?");
        }
        sql.append(")");
        return sql.toString();
    }

    // update account set name = ? where id = ?
    private String makeUpdateSql(String tableName, Map<String, Object> data, List<Object> values) {
        List<Object> idValue = new ArrayList<>();
        List<String> setList = new ArrayList<>();
        for(String name : data.keySet()) {
            Object object = data.get(name);
            if(object!=null){
                if(name.equals("id")){
                    idValue.add(data.get(name));
                }else{
                    setList.add(StrUtil.toUnderlineCase(name)+"=?");
                    values.add(data.get(name));
                }
            }
        }
        values.addAll(idValue);
        String setSqlStr = StrUtil.join(",", setList);
        return String.format("update %s set %s where id = ?", tableName, setSqlStr, values);
    }
}
